package com.cmc.cloud.cmclink.doc.service.impl;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javax.annotation.Resource;

import org.apache.tika.utils.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.cmc.cloud.cmclink.doc.api.document.emums.PackGroupEnum;
import com.cmc.cloud.cmclink.doc.constants.CommonConstants;
import com.cmc.cloud.cmclink.doc.constants.DgCommodityConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.DgCommodityErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.convert.DgCommodityConvert;
import com.cmc.cloud.cmclink.doc.dto.DgCommodityGetDto;
import com.cmc.cloud.cmclink.doc.entity.DgCommodityDO;
import com.cmc.cloud.cmclink.doc.entity.DgCommodityPsnDO;
import com.cmc.cloud.cmclink.doc.mapper.DgCommodityMapper;
import com.cmc.cloud.cmclink.doc.service.DgCommodityPsnService;
import com.cmc.cloud.cmclink.doc.service.DgCommodityService;
import com.cmc.cloud.cmclink.doc.util.sepcial.DgCommodityUtil;
import com.cmc.cloud.cmclink.doc.vo.special.dgcommodityvo.DgCommodityImportExcelVO;
import com.cmc.cloud.cmclink.doc.vo.special.dgcommodityvo.DgCommodityPageReqVO;
import com.cmes.framework.common.exception.ErrorCode;
import com.cmes.framework.common.pojo.PageResult;
import com.cmes.framework.ex.Assertion;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;

import lombok.extern.slf4j.Slf4j;

/**
 * 危险品品名库 Service 实现类
 *
 * @author 集运管理员
 */
@Service
@Validated
@Slf4j
public class DgCommodityServiceImpl implements DgCommodityService {

    @Resource
    private DgCommodityMapper dgCommodityMapper;

    @Resource
    private DgCommodityPsnService dgCommodityPsnService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DgCommodityGetDto getDgCommodity(Long id) {
        DgCommodityDO dgCommodityDO = dgCommodityMapper.selectById(id);
        List<DgCommodityPsnDO> dgProhibitedPsnsDO = dgCommodityPsnService.getDgCommodityPsnsByMainId(id);
        DgCommodityGetDto dgCommodityGetDto = new DgCommodityGetDto();
        dgCommodityGetDto.setDgCommodityDO(dgCommodityDO);
        dgCommodityGetDto.setDgCommodityPsnDOList(dgProhibitedPsnsDO);
        return dgCommodityGetDto;
    }

    @Override
    public PageResult<DgCommodityDO> getDgCommodityPage(DgCommodityPageReqVO reqVO) {
        String versionNo = reqVO.getVersionNo();
        LambdaQueryWrapperX<DgCommodityDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.eqIfPresent(DgCommodityDO::getImoClass, reqVO.getImoClass()).eqIfPresent(DgCommodityDO::getUnNo,
            reqVO.getUnNo());
        if (StringUtils.isEmpty(versionNo)) {
            queryWrapperX.eq(DgCommodityDO::getEnable, true);
        } else {
            DgCommodityUtil.validVersionValid(versionNo);
            queryWrapperX.eq(DgCommodityDO::getVersionNo, versionNo);
        }

        return dgCommodityMapper.selectPage(reqVO, queryWrapperX);
    }

    @Override
    public List<DgCommodityDO> getExportList(String versionNo) {
        LambdaQueryWrapperX<DgCommodityDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.eq(DgCommodityDO::getVersionNo, versionNo);
        return dgCommodityMapper.selectList(queryWrapperX);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer saveImportData(List<DgCommodityImportExcelVO> exportVoList, String versionNo) {
        // 验证是否有重复元素
        validRepeatItem(exportVoList);

        // 删除该版本数据
        deleteSameVersion(versionNo);

        // 检查当前导入是否最新版本，若是则停用之前版本
        boolean isNewVersion = checkNewAndUpdateVersion(versionNo);

        // 将集合分成2000一组 去保存
        int batchSize = DgCommodityConstants.EXCEL_SAVE_BATCH;
        List<List<DgCommodityImportExcelVO>> partitions =
            IntStream.range(0, (exportVoList.size() + batchSize - 1) / batchSize)
                .mapToObj(i -> exportVoList.subList(i * batchSize, Math.min(batchSize * (i + 1), exportVoList.size())))
                .collect(Collectors.toList());

        partitions.forEach(partition -> {
            // 处理每个子集合
            partition.forEach(entity -> {
                dealWithPackingGroup(entity);
                validExcelParam(entity);
                entity.setVersionNo(versionNo);
            });

            List<DgCommodityDO> dgCommodityDOList = DgCommodityConvert.INSTANCE.convertExcelDOList(partition);
            // mapstruct中mapping未生效
            dgCommodityDOList.forEach(dgCommodityDO -> dgCommodityDO.setEnable(isNewVersion));
            log.info("insertBatch db begin, count is : {}, beginTime is : {}", dgCommodityDOList.size(),
                LocalDateTime.now());
            dgCommodityMapper.insertBatch(dgCommodityDOList);
            log.info("insertBatch db end, endTime is : {}", LocalDateTime.now());
        });
        return exportVoList.size();
    }

    private void dealWithPackingGroup(DgCommodityImportExcelVO entity) {
        String packingGroup = entity.getPackingGroup();
        if (StringUtils.isEmpty(packingGroup)) {
            return;
        }
        Map<String, String> numberMap = new HashMap<>(CommonConstants.DEFAULT_MAP_COUNT);
        numberMap.put(String.valueOf(1), PackGroupEnum.ONE.getValue());
        numberMap.put(String.valueOf(CommonConstants.TWO), PackGroupEnum.TWO.getValue());
        numberMap.put(String.valueOf(CommonConstants.THREE), PackGroupEnum.THREE.getValue());
        numberMap.put("Ⅰ", PackGroupEnum.ONE.getValue());
        numberMap.put("Ⅱ", PackGroupEnum.TWO.getValue());
        numberMap.put("Ⅲ", PackGroupEnum.THREE.getValue());
        numberMap.put("I", PackGroupEnum.ONE.getValue());
        numberMap.put("II", PackGroupEnum.TWO.getValue());
        numberMap.put("III", PackGroupEnum.THREE.getValue());
        if (numberMap.containsKey(packingGroup)) {
            entity.setPackingGroup(numberMap.get(packingGroup));
        }
    }

    private void validRepeatItem(List<DgCommodityImportExcelVO> exNoDupList) {
        Map<String, Integer> uniqMap = new HashMap<>(Math.max(CommonConstants.DEFAULT_MAP_COUNT, exNoDupList.size()));
        for (DgCommodityImportExcelVO excelVO : exNoDupList) {
            String key = buildDgCommodityKey(excelVO);
            if (uniqMap.containsKey(key)) {
                Assertion.assertTrue(false).raise0(DgCommodityErrorCodeConstants.DG_COMMODITY_IMPORT_REPEAT.getCode(),
                    String.valueOf(excelVO.getRowNumber()), String.valueOf(uniqMap.get(key)));
            } else {
                uniqMap.put(key, excelVO.getRowNumber());
            }
        }
    }

    private String buildDgCommodityKey(DgCommodityImportExcelVO excelVO) {
        StringBuilder sb = new StringBuilder();
        if (excelVO.getUnNo() != null) {
            sb.append(excelVO.getUnNo());
        }
        if (excelVO.getPackingGroup() != null) {
            sb.append(excelVO.getPackingGroup());
        }
        if (excelVO.getSeq() != null) {
            sb.append(excelVO.getSeq());
        }
        if (excelVO.getPsn() != null) {
            sb.append(excelVO.getPsn());
        }
        return sb.toString();
    }

    @Override
    public Boolean versionExist(String versionNo) {
        LambdaQueryWrapperX<DgCommodityDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.eq(DgCommodityDO::getVersionNo, versionNo);
        Long count = dgCommodityMapper.selectCount(queryWrapperX);
        return count != null && count != 0;
    }

    private void deleteSameVersion(String versionNo) {
        LambdaQueryWrapperX<DgCommodityDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.eq(DgCommodityDO::getVersionNo, versionNo);
        dgCommodityMapper.delete(queryWrapperX);
    }

    private boolean checkNewAndUpdateVersion(String versionNo) {
        String maxDbVersion = getMaxVersion();
        if (maxDbVersion == null) {
            return true;
        }
        if (versionNo.compareTo(maxDbVersion) >= 0) {
            // 导入最新版本，停用之前版本
            updateOlderVersionDisable();
            return true;
        }

        return false;
    }

    private String getMaxVersion() {
        LambdaQueryWrapperX<DgCommodityDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.orderByDesc(DgCommodityDO::getVersionNo).last("LIMIT 1");
        List<DgCommodityDO> dgCommodityDOList = dgCommodityMapper.selectList(queryWrapper);
        if (!dgCommodityDOList.isEmpty()) {
            return dgCommodityDOList.get(0).getVersionNo();
        }
        return null;
    }

    private void updateOlderVersionDisable() {
        LambdaUpdateWrapper<DgCommodityDO> updateWrapperX = new LambdaUpdateWrapper<>();
        updateWrapperX.set(DgCommodityDO::getEnable, false);
        dgCommodityMapper.update(null, updateWrapperX);
    }

    private void validExcelParam(DgCommodityImportExcelVO vo) {
        if (StringUtils.isEmpty(vo.getUnNo()) || vo.getUnNo().length() > DgCommodityConstants.UN_NO_LENGTH) {
            Assertion.assertTrue(false).raise0(DgCommodityErrorCodeConstants.DG_COMMODITY_UN_NO_INVALID.getCode(),
                String.valueOf(vo.getRowNumber()), String.valueOf(DgCommodityConstants.UN_NO_LENGTH));
        }

        if (StringUtils.isEmpty(vo.getPsn()) || vo.getPsn().length() > DgCommodityConstants.PSN_LENGTH) {
            Assertion.assertTrue(false).raise0(DgCommodityErrorCodeConstants.DG_COMMODITY_PSN_INVALID.getCode(),
                String.valueOf(vo.getRowNumber()), String.valueOf(DgCommodityConstants.PSN_LENGTH));
        }

        if (vo.getSeq() == null) {
            Assertion.assertTrue(false).raise0(DgCommodityErrorCodeConstants.DG_COMMODITY_SEQ_INVALID.getCode(),
                String.valueOf(vo.getRowNumber()));
        }

        validExcelParam1(vo);
        validExcelParam2(vo);
    }

    private void validExcelParam1(DgCommodityImportExcelVO vo) {
        validPackGroup(vo.getPackingGroup(), vo.getRowNumber());

        validLength(vo.getImoClass(), DgCommodityErrorCodeConstants.DG_COMMODITY_IMO_CLASS_INVALID,
            DgCommodityConstants.IMO_CLASS_LENGTH, vo.getRowNumber());

        validLength(vo.getCmptGroup(), DgCommodityErrorCodeConstants.DG_COMMODITY_CMPT_GROUP_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getSubsidiaryRisk(), DgCommodityErrorCodeConstants.DG_COMMODITY_SUBSIDIARY_RISK_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getStowageCategory(), DgCommodityErrorCodeConstants.DG_COMMODITY_STOWAGE_CATEGORY_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getExtImdg(), DgCommodityErrorCodeConstants.DG_COMMODITY_EXT_IMDG_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getSegregationGroup(), DgCommodityErrorCodeConstants.DG_COMMODITY_SEGREGATION_GROUP_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getSegregationTable(), DgCommodityErrorCodeConstants.DG_COMMODITY_SEGREGATION_TABLES_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getSegregationTable2(), DgCommodityErrorCodeConstants.DG_COMMODITY_SEGREGATION_TABLES2_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getSpecialProv(), DgCommodityErrorCodeConstants.DG_COMMODITY_SPECIAL_PROV_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getMp(), DgCommodityErrorCodeConstants.DG_COMMODITY_MP_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getClearLiving(), DgCommodityErrorCodeConstants.DG_COMMODITY_CLEAR_LIVING_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getFoodstuffs(), DgCommodityErrorCodeConstants.DG_COMMODITY_FOOD_STUFFS_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getHeatSource(), DgCommodityErrorCodeConstants.DG_COMMODITY_HEAT_SOURCE_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getShadeRadient(), DgCommodityErrorCodeConstants.DG_COMMODITY_SHADE_RADIENT_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(String.valueOf(vo.getFlashPoint()), DgCommodityErrorCodeConstants.DG_COMMODITY_FLASH_POINT_INVALID,
            DgCommodityConstants.FLASH_POINT_LENGTH, vo.getRowNumber());

        validLength(vo.getStowageSegregation(), DgCommodityErrorCodeConstants.DG_COMMODITY_STOWAGE_SEGREGATION_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());
    }

    private void validExcelParam2(DgCommodityImportExcelVO vo) {
        validLength(vo.getLimitedQty(), DgCommodityErrorCodeConstants.DG_COMMODITY_LIMITD_QTY_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getPackInstr(), DgCommodityErrorCodeConstants.DG_COMMODITY_PACK_INSTR_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getPackProv(), DgCommodityErrorCodeConstants.DG_COMMODITY_PACK_PROV_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getIbcInstr(), DgCommodityErrorCodeConstants.DG_COMMODITY_IBC_INSTR_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getIbcProv(), DgCommodityErrorCodeConstants.DG_COMMODITY_IBC_PROV_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getTankImo(), DgCommodityErrorCodeConstants.DG_COMMODITY_TANK_IMO_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getTankUn(), DgCommodityErrorCodeConstants.DG_COMMODITY_TANK_UN_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getTankProv(), DgCommodityErrorCodeConstants.DG_COMMODITY_TANK_PROV_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getEms(), DgCommodityErrorCodeConstants.DG_COMMODITY_EMS_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getNotMixClass1(), DgCommodityErrorCodeConstants.DG_COMMODITY_NOT_MIX_CLASS1_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getCargoType(), DgCommodityErrorCodeConstants.DG_COMMODITY_CARGO_TYPE_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getFpGroup(), DgCommodityErrorCodeConstants.DG_COMMODITY_FP_GROUP_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getStowageHandling(), DgCommodityErrorCodeConstants.DG_COMMODITY_STOWAGE_HANDLING_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getSegregation(), DgCommodityErrorCodeConstants.DG_COMMODITY_SEGREGATION_INVALID,
            DgCommodityConstants.COMMON_LENGTH_100, vo.getRowNumber());

        validLength(vo.getNos(), DgCommodityErrorCodeConstants.DG_COMMODITY_NOS_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getBusinessId(), DgCommodityErrorCodeConstants.DG_COMMODITY_BUSINESS_ID_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());

        validLength(vo.getBusinessId1(), DgCommodityErrorCodeConstants.DG_COMMODITY_BUSINESS_BUSINESS_ID1_INVALID,
            DgCommodityConstants.COMMON_LENGTH_50, vo.getRowNumber());
    }

    private void validLength(String field, ErrorCode errorCode, int length, int rowNumber) {
        if (!StringUtils.isEmpty(field) && field.length() > length) {
            Assertion.assertTrue(false).raise0(errorCode.getCode(), String.valueOf(rowNumber), String.valueOf(length));
        }
    }

    private void validPackGroup(String field, int rowNumber) {
        if (StringUtils.isEmpty(field)) {
            return;
        }
        ErrorCode errorCode = DgCommodityErrorCodeConstants.DG_COMMODITY_PACKING_GROUP_INVALID;
        if (!PackGroupEnum.ONE.getValue().equals(field) && !PackGroupEnum.TWO.getValue().equals(field)
            && !PackGroupEnum.THREE.getValue().equals(field)) {
            Assertion.assertTrue(false).raise0(errorCode.getCode(), String.valueOf(rowNumber));
        }
    }
}
